package com.cirnoworks.utils.text;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;

public abstract class AbstractTextArea {

	private static final ArrayDeque<Link> linkPool = new ArrayDeque<Link>();

	protected final ArrayList<Link> links = new ArrayList<Link>();

	private final List<Link> linksView = Collections.unmodifiableList(links);

	public void addLink(float x, float y, float w, float h, int linkId) {
		Link link;
		synchronized (AbstractTextArea.class) {
			link = linkPool.pollFirst();
		}
		if (link == null) {
			link = new Link();
		}
		link.setup(x, y, w, h, linkId);
		links.add(link);
	}

	public void clearLinks() {
		if (links.size() > 0) {
			synchronized (AbstractTextArea.class) {
				for (int i = 0, max = links.size(); i < max; i++) {
					linkPool.addLast(links.get(i));
				}
			}
			links.clear();
		}
	}

	public List<Link> getLinks() {
		return linksView;
	}

	protected final ArrayList<Line> lines = new ArrayList<Line>();
	protected float width, height;
	protected boolean dirty = true;
	protected boolean noBreak;

	public boolean isNoBreak() {
		return noBreak;
	}

	public void setNoBreak(boolean nobreak) {
		this.noBreak = nobreak;
	}

	public float getWidth() {
		return width;
	}

	public void setWidth(float width) {
		this.width = width;
	}

	public float getHeight() {
		return height;
	}

	public void setHeight(float height) {
		this.height = height;
	}

	protected abstract void setupFragment(TextFragment fragment);

	protected abstract float measureText(TextFragment fragment, int begin,
			int end);

	private final MeasureCache<Float> measureCache = new MeasureCache<Float>(
			1024);

	protected final float cachedMeasureText(TextFragment fragment, int begin,
			int end) {
		this.tf = fragment;
		this.tfBegin = begin;
		this.tfEnd = end;
		return measureCache.putOnNotExist(fragment, begin, end, measure);
	}

	private TextFragment tf;
	private int tfBegin;
	private int tfEnd;

	private Callable<Float> measure = new Callable<Float>() {

		@Override
		public Float call() throws Exception {
			return measureText(tf, tfBegin, tfEnd);
		}

	};

	protected int defaultBreakText(TextFragment fragment, float width,
			float[] outputWidth) {
		float mw = 0, lastMW = 0;
		for (int i = 0, max = fragment.length() + 1; i < max; i++) {
			mw = cachedMeasureText(fragment, 0, i);
			if (mw > width) {
				if (outputWidth != null && outputWidth.length > 0) {
					outputWidth[0] = lastMW;
				}
				return i - 1;
			}
			lastMW = mw;
		}
		if (outputWidth != null && outputWidth.length > 0) {
			outputWidth[0] = lastMW;
		}
		return fragment.length();
	}

	protected abstract int breakText(TextFragment fragment, float width,
			float[] outputWidth);

	public void clear() {
		dirty = true;
		noBreak = false;
		lines.clear();
	}

	protected void appendLine(List<TextFragment> textFragments) {
		dirty = true;
		float leftWidth = width;
		Line line = new Line();
		float[] outWidth = new float[1];
		int pos;
		for (TextFragment fragment : textFragments) {
			if (!noBreak) {
				while ((pos = breakText(fragment, leftWidth, outWidth)) != fragment
						.length()) {
					if (pos > 0) {
						TextFragment frag = fragment.subSequence(0, pos);
						// 将当前的碎片分裂，前面一半放到当前Line，后面一半放到下一Line
						if (frag.getAscent() == Float.NEGATIVE_INFINITY
								|| frag.getDescent() == Float.NEGATIVE_INFINITY) {
							setupFragment(frag);
						}
						line.addFragment(frag);
						fragment = fragment.subSequence(pos, fragment.length());
					}
					lines.add(line);
					line = new Line();
					leftWidth = width;
				}
			}
			if (fragment.getAscent() == Float.NEGATIVE_INFINITY
					|| fragment.getDescent() == Float.NEGATIVE_INFINITY) {
				setupFragment(fragment);
			}
			line.addFragment(fragment);
			leftWidth -= outWidth[0];
		}
		if (line.fragmentsCount() > 0) {
			lines.add(line);
		}
	}

	public void append(char ch, int fontId, int size, int color, int misc,
			int linkId) {
		if (ch == '\n') {
			if (!noBreak) {
				lines.add(new Line());
			}
		} else if (lines.size() > 0) {
			// FIXME 这点可以优化，将删除并重新添加的粒度优化到TextFragment级别……当然上面的函数也得做对应的修改
			Line lastLine = lines.remove(lines.size() - 1);
			List<TextFragment> frags = lastLine.getFragments();
			TextFragment lastFragment = frags.size() == 0 ? null : frags
					.get(frags.size() - 1);
			if (lastFragment != null
					&& lastFragment.isSameStyleWith(fontId, size, color, misc,
							linkId)) {
				lastFragment.append(ch);
				appendLine(frags);
			} else {
				TextFragment frag = new TextFragment(ch);
				frag.setFontId(fontId);
				frag.setSize(size);
				frag.setColor(color);
				frag.setMisc(misc);
				frag.setLinkId(linkId);
				setupFragment(frag);
				lastLine.addFragment(frag);
				appendLine(lastLine.getFragments());
			}
		} else {
			List<TextFragment> frags = new ArrayList<TextFragment>(4);
			TextFragment frag = new TextFragment(ch);
			frag.setFontId(fontId);
			frag.setSize(size);
			frag.setColor(color);
			frag.setMisc(misc);
			frag.setLinkId(linkId);
			frags.add(frag);
			appendLine(frags);
		}
	}

	public void append(CharSequence chars, int fontId, int size, int color,
			int misc, int linkId) {
		for (int i = 0, max = chars.length(); i < max; i++) {
			append(chars.charAt(i), fontId, size, color, misc, linkId);
		}
	}
}
